package Casillas;

import ia1_practica2.mainFrame;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import Manhattan.StateOrder;

public class Casillas {

    String str = ""; // initial state
    String goal = ""; //goal state

    PriorityQueue <StateOrder> queue;
    Map<String,Integer> levelDepth;
    Map<String,String> stateHistory;

    int nodes = 0; //counter for node generation
    int limit = 1000; //counter for limit
    int unique = -1;//counter for uniq state
    int newValue; //counter depth limit
    int a; //position of blank
    int h; //heuristic

    String currState;
    boolean solution = false;
    int countStep=0;
    
    public Casillas(String str,String goal, int limit){
        queue = new PriorityQueue <StateOrder> ();
        levelDepth = new HashMap<String, Integer>();
        stateHistory = new HashMap<String,String>();
        this.str = str;
        this.goal = goal;
        this.limit = limit;
        addToQueue(str,null);
        
    }

    public void doSearch (){
        while (!queue.isEmpty()){
            currState = queue.poll().toString();//RETRIEVE then remove first node
            if (currState.equals(goal)){ // check if current state is goal state
                solution = true;
                printSolution(currState);// print solutions
                break;
            }
            
            if (levelDepth.get(currState) == limit){//check if under limit
                solution = false;
                printSolution(currState);// print solutions
                break;
            }
            else {
                //expand currentstate then add expanded node to the of openlist
                a = currState.indexOf("0");// get index position of 0 (blank)
                //left
                while (a != 0 && a != 3 && a != 6){// if blank not in the left most column then it able move left
                    String nextState = currState.substring(0,a-1)+"0"+currState.charAt(a-1)+currState.substring(a+1);//swap blank with destination
                    addToQueue(nextState, currState);//add expanded node to openlist
                    nodes++;
                    break;
                }
                //up
                while (a!=0 && a!=1 && a!=2){//if blank not in the very top of row then it able to move up
                    String nextState = currState.substring(0,a-3)+"0"+currState.substring(a-2,a)+currState.charAt(a-3)+currState.substring(a+1);//swap blank with destination
                    addToQueue(nextState, currState);//add expanded node to openlist
                    nodes++; //nodes = nodes + 1; a node is being genereted add it to counter
                    break;
                }
                //right
                while(a != 2 && a != 5 && a != 8){// if blank not in the right most column then it able to move right
                    String nextState = currState.substring(0,a)+currState.charAt(a+1)+"0"+currState.substring(a+2);//swap blank with destination
                    addToQueue(nextState, currState);//add expanded node to openlist
                    nodes++;
                    break;
                }
                //down
                while (a != 6 && a != 7 && a != 8) {// if blank not in the very bottom row then it able to move down
                    String nextState = currState.substring(0,a)+currState.substring(a+3,a+4)+currState.substring(a+1,a+3)+"0"+currState.substring(a+4);//swap blank with destination
                    addToQueue(nextState, currState);//add expanded node to openlist
                    nodes++;
                    break;
                }
            }
        }
        if (solution){
            System.out.println("Solucion Encontrada");
        }
        else {
            System.out.println("No se encontro una solucion con el limite establecido:");
            System.out.println("1. Trate de incrementar el limite");
            System.out.println("2. Use otra heuristica");
            System.out.println("3. Podria se que sea imposible resolverlo");
        }
    }

    private void addToQueue (String newState, String oldState){
        if(!levelDepth.containsKey(newState)){// check repeated state
            newValue = oldState == null ? 0 : levelDepth.get(oldState) + 1;
            unique ++;
            levelDepth.put(newState, newValue);
            h = calcMismatch(newState,goal); // calculate heuristic from newstate
            queue.add(new StateOrder(h,newState));//add to
            stateHistory.put(newState, oldState);
        }
    }

    int calcMismatch (String currState, String goalState){
        int mismatch = 0;
        for (int i=1;i<9;i++){
            if (currState.indexOf(String.valueOf(i))!= goalState.indexOf(String.valueOf(i))){
                mismatch++;
            }
        }
        return mismatch;
    }

    public String[] printSolution (String currState){
        String salida[];
                
        if (solution){
            
            countStep = levelDepth.get(currState);
            salida= new String[3];
            System.out.println("La solucion encontrada es: " +levelDepth.get(currState)+" nivel(es)");
            System.out.println("Nodos Generados: "+ nodes);
            System.out.println("Nodos unicos generados: "+ unique);
        }
        else{
            countStep = levelDepth.get(currState);
            salida=new String[4];
            System.out.println("Nodos Generados: "+ nodes);
            System.out.println("No hay Solucion Sorry :( !");
            System.out.println("Ya llego al limite!");
            System.out.println("Nodos unicos generados: "+ unique);
            
            mainFrame.printArea("Solucion no Encontrada!");
            mainFrame.printArea("Se alcanzo el Limite de iteracciones!");
            mainFrame.printArea("");
        }
        String ruta = "";
        String traceState = currState;
        while (traceState != null) {
            //System.out.println(traceState + " del paso " + levelDepth.get(traceState));
            mainFrame.printArea(traceState + " del paso " + levelDepth.get(traceState));
            try{
                for(int z=0;z<9;z++){
                    //System.out.print(" " + String.valueOf(traceState.charAt(z)) + " ");
                    ruta = ruta + " " + String.valueOf(traceState.charAt(z)) + " ";
                    if ((z+1) % 3 == 0){
                        //System.out.println();
                        mainFrame.printArea(ruta);
                        ruta = "";
                    }
                }
            }
            catch (NullPointerException e) {}
            traceState = stateHistory.get(traceState);
        }
        if(solution){javax.swing.JOptionPane.showMessageDialog(null, "Solucion encontrada!!");}
        return salida;
    }

    
     public int getNodes() {
        return nodes;
    }

    public int getUnique() {
        return unique;
    }

    public int getH() {
        return h;
    }

    public int getCountStep() {
        return countStep;
    }
}